//-----------------------------------------------------------------------------
// Filename: SIPProxyWebService.cs
//
// Description: Sets SIP proxy services for the SIP Switch: IsAlive, GetActiveCalls, AddSIPRegistration etc.
// 
// History:
// 22 Nov 2006	Aaron Clauson	    Created.
// 24 April 2007  Guillaume Bonnet    Edited : switchboard functions: call display, hold/resume, transfer/forward
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Net;
using System.ServiceModel;
using System.Text;
using System.Data;
using System.Text.RegularExpressions;
using System.Timers;
using System.Threading;
using System.Web.Services;
//using Aza.Configuration;
//using Aza.Configuration.WebServices;
using CN.VoIP.Net;
using CN.VoIP.Net.SIP;
using CN.VoIP.SIPServer;
using log4net;
using CN.Sys;
using CN.Sys.WebServices;
using CN.Sys.Net;

namespace CN.VoIP.SIPServerCores.StatefulProxy
{  
    [ServiceContract]
    public class SIPProxyWebService
    {
        private const string SERVER_STRING = "sip.CNSIPSwitch.com";
        
        private static string m_CRLF = SIPConstants.CRLF;
        
        private static ILog logger = log4net.LogManager.GetLogger("sipproxy");

        public static event ProxyLogDelegate StatefulProxyLogEvent;
        public static ProxyMonitorChannel m_monitorChannel;

        public int m_currentTimeSec = DateTime.Now.Second;      // Current time, we need that to calculate the call duration live
        public int m_currentTimeMin = DateTime.Now.Minute;
        public int m_currentTimeHou = DateTime.Now.Hour;

        [OperationContract]
        public bool IsAlive()
        {
            return true;
        }

        /// <summary>
        /// Returns an XML wrapped list of the calls that are currently in progress for the specified user.
        /// </summary>
        [OperationContract]
        public XMLServiceResultStruct GetActiveCalls(string username)
        {
            XMLServiceResultStruct opResult = new XMLServiceResultStruct(true, "Sorry an unknown error has occurred processing your request. Please try reloading the page and if the error persists contact support@blueface.ie.", null);
            
            try
            {
                List<SIPSwitchCall> userCalls = SIPSwitchCall.GetCurrentCalls(username);

                if (userCalls != null && userCalls.Count > 0)
                {
                    string callURIs = "<callsinprogress>";
                    
                    foreach (SIPSwitchCall userCall in userCalls)
                    {
                        // Timer update
                        SIPSwitchCall call = SIPSwitchCall.GetCall(username, userCall.SwitchCallId);    // Retreive call information

                        int callDurationSec = m_currentTimeSec - call.SwitchedCall.Second;              // call duration calculation
                        int callDurationMin = m_currentTimeMin - call.SwitchedCall.Minute;
                        int callDurationHou = m_currentTimeHou - call.SwitchedCall.Hour;

                        if (callDurationSec < 0)
                        {
                            callDurationSec = 60 + callDurationSec;     // in that case with have to adjust the minutes timer with -1 min 
                            callDurationMin = callDurationMin - 1;      // otherwise the timer will display one minute in extra
                        }
                        if (callDurationMin < 0)
                        {
                            callDurationMin = 60 + callDurationMin;
                            callDurationHou = callDurationHou - 1;
                        }
                        if (callDurationHou < 0)
                        {
                            callDurationHou = 24 + callDurationHou;
                        }
                        string timer = null;                            // In order to display a timer of this pattern: hh:mm:ss, I need to edit it in a string
                        timer += (callDurationHou < 10) ? "0" + callDurationHou.ToString() : callDurationHou.ToString();
                        timer += ":";
                        timer += (callDurationMin < 10) ? "0" + callDurationMin.ToString() : callDurationMin.ToString(); 
                        timer += ":";
                        timer += (callDurationSec < 10) ? "0" + callDurationSec.ToString() : callDurationSec.ToString();

                        string arrow = (call.SwitchedCall.IsOutgoingCall == true) ? "rightarrow.png" : "leftarrow.png";

                        string callStatus = (call.m_callInProgress == true) ? "In Progress" : "Active";

                        callURIs +=
                            " <call>" +
                            "  <switchcallid>" + userCall.SwitchCallId + "</switchcallid>" + 
                            "  <clienturi>" + userCall.ClientTransaction.RequestURI.CanonicalAddress + "</clienturi>" +
                            "  <clientfromname>" + userCall.ClientTransaction.RequestFrom.Name + "</clientfromname>" +
                            "  <clientfromuri>" + userCall.ClientTransaction.RequestFrom.URI.CanonicalAddress + "</clientfromuri>" +
                            "  <clientsocket>" + IPSocket.GetSocketString(userCall.ClientTransaction.InitialRequestEndPoint) + "</clientsocket>" +
                            "  <arrow>" + arrow + "</arrow>" +
                            "  <callStatus>" + callStatus + "</callStatus>" +
                            "  <serveruri>" + SIPURI.ParseSIPURI(userCall.SwitchedCall.Uri).CanonicalAddress + "</serveruri>" +
                            "  <serverfromname>" + userCall.ServerTransaction.RequestFrom.Name + "</serverfromname>" +
                            "  <serverfromuri>" + userCall.ServerTransaction.RequestFrom.URI.CanonicalAddress + "</serverfromuri>" +
                            "  <serversocket>" + IPSocket.GetSocketString(userCall.ServerEndPoint) + "</serversocket>" +
                            "  <duration>" + timer + "</duration>" +
                            " </call>"; 
                    } // end foreach
                    callURIs += "</callsinprogress>";

                    string resultMessage = null;
                    if (userCalls.Count == 1)
                    {
                        resultMessage = "1 active call found for " + username + ".";
                    }
                    else
                    {
                        resultMessage = userCalls.Count + " calls found for " + username + ".";
                    }

                    opResult = new XMLServiceResultStruct(false, resultMessage, callURIs);
                }
                else
                {
                    opResult = new XMLServiceResultStruct(false, "No active calls found for " + username + ".", "<callsinprogress/>");
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetActiveCalls. " + excp.Message);
                opResult.Message = "Exception. " + excp.Message;
            }

            return opResult;
        }
        
        [OperationContract]
        public string GetCallDetails(string callId)
        {
            return null;
        }

        [OperationContract]
        public void AddSIPRegistration(UserRegistration userRegistration)
        {
            try
            {
                logger.Debug("AddSIPRegistration webservice called.");

                SIPRegistrationAgent.AddRegistration(userRegistration);
            }
            catch (Exception excp)
            {
                logger.Error("Exception AddUserRegistration. " + excp.Message);
                throw excp;
            }
        }

        [OperationContract]
        public void DeleteSIPRegistration(Guid registrationId)
        {
            try
            {
                logger.Debug("DeleteUserRegistration webservice called for " + registrationId + ".");
                RemoveSIPContact(registrationId, null);

                SIPRegistrationAgent.DeleteRegistration(registrationId);
            }
            catch (Exception excp)
            {
                logger.Error("Exception DeleteUserRegistration. " + excp.Message);
                throw excp;
            }
        }

        [OperationContract]
        public void DeleteAllUserSIPRegistrations(string owner)
        {
            try
            {
                logger.Debug("DeleteAllUserSIPRegistrations webservice called for " + owner + ".");

                List<UserRegistration> userRegistrations = GetCurrentSIPRegistrations(owner);

                if (userRegistrations != null && userRegistrations.Count > 0)
                {
                    foreach (UserRegistration userRegistration in userRegistrations)
                    {
                        userRegistration.DeleteRequired = true;
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception DeleteAllUserSIPRegistrations. " + excp.Message);
                throw excp;
            }
        }

        [OperationContract]
        public List<UserRegistration> GetCurrentSIPRegistrations(string owner)
        {
            try
            {
                return SIPRegistrationAgent.GetOwnerRegistrations(owner);
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetCurrentSIPRegistrations. " + excp.Message);
                throw excp;
            }
        }

        [OperationContract]
        public List<UserRegistration> RemoveSIPContact(Guid registrationId, string contactToRemove)
        {
            try
            {
                logger.Debug("RemoveRegistrarContact webservice called for " + registrationId + " and " + contactToRemove + ".");

                UserRegistration registration = SIPRegistrationAgent.Get(registrationId);

                if (registration != null)
                {
                    // Look through the current contacts to try and find one to match this contact.
                    foreach (SIPContactHeader contact in registration.ContactsList)
                    {
                        logger.Debug("Comparing: " + contact.ContactURI.ToString() + " to " + contactToRemove);

                        if (contactToRemove == null || contact.ContactURI.ToString() == contactToRemove)
                        {
                            logger.Debug("Attempting removal of registrar contact " + contact.ContactURI.ToString() + ".");
                            SIPRegistrationAgent.SendContactRemovalRequest(registrationId, contact.ContactURI.ToString());

                            // Give the request a chance to be processed.
                            Thread.Sleep(1000);
                        }
                    }

                    return SIPRegistrationAgent.GetOwnerRegistrations(registration.Owner);
                }
                else
                {
                    throw new ApplicationException("Could not find the registration record.");
                }

            }
            catch (Exception excp)
            {
                logger.Error("Exception RemoveRegistrarContact. " + excp.Message);
                throw excp;
            }
        }

        [OperationContract]
        public SIPRegistrarRecord GetSwitchRegistration(SIPParameterlessURI addressOfRecord)
        {
            try
            {
                if (addressOfRecord != null)
                {
                    return SIPRegistrations.Lookup(addressOfRecord);
                }
                else
                {
                    logger.Warn("GetSwitchRegistration was called with an empty address-of-record.");
                    return null;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetSwitchRegistration. " + excp.Message);
                throw excp;
            }
        }

        /// <summary>
        /// Send a Message Waiting Indicator request to the specified user agent URI. If setMWI is true the number of messages indicated will
        /// be one otherwise 0.
        /// </summary>
        [OperationContract]
        public void SendMWIRequest(string userAgentURI, bool setMWI, string username)
        {
            try
            {
                logger.Debug("Attempting SendMWIRequest for " + userAgentURI + " as " + setMWI + ".");

                SIPRequest mwiRequest = SIPNotificationAgent.GetNotifyRequest(SIPTransport.GetTransportContact(null), null, SIPURI.ParseSIPURI(userAgentURI), username, setMWI);
                SIPTransaction mwiTransaction = new SIPTransaction(mwiRequest);
                mwiTransaction.TransactionFinalResponseReceived += new SIPTransactionResponseReceived(MWIFinalResponseReceived);

                FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.MWI, "Sending " + setMWI + " mwi to " + SIPURI.ParseSIPURI(userAgentURI).CanonicalAddress, username));

                SIPTransport.SendRequest(mwiRequest);
            }
            catch (Exception excp)
            {
                logger.Error("Exception SendMWIRequest. " + excp.Message + ".");
            }
        }

        private void MWIFinalResponseReceived(IPEndPoint localEndPoint,  IPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
        {
            try
            {
                string username = sipResponse.Header.From.FromURI.User;

                logger.Debug("MWI response received from " + IPSocket.GetSocketString(remoteEndPoint) + " " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + username + ".");

                FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.MWI, "MWI response received from " + IPSocket.GetSocketString(remoteEndPoint) + " " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase, username));
            }
            catch (Exception excp)
            {
                logger.Error("Exception MWIFinalResponseReceived. " + excp.Message);
            }
        }

        /// <summary>
        /// Send a Message Waiting Indicator request to the specified user agent URI. If setMWI is true the number of messages indicated will
        /// be one otherwise 0.
        /// </summary>
        [OperationContract]
        public void SendInviteRequest(string userAgentURI, string callerIdName, string callerIdUser, string username)
        {
            try
            {
                logger.Debug("Attempting SendInviteRequest for " + userAgentURI + " as " + callerIdName + " and " + callerIdUser  + ".");

                SIPRequest inviteRequest = GetInviteRequest(SIPURI.ParseSIPURI(userAgentURI), callerIdName, callerIdUser, SERVER_STRING);
                UACInviteTransaction inviteTransaction = new UACInviteTransaction(inviteRequest);
                inviteTransaction.TransactionFinalResponseReceived += new SIPTransactionResponseReceived(InviteFinalResponseReceived);

                FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Sending INVITE to " + SIPURI.ParseSIPURI(userAgentURI).ToString(), username));

                inviteTransaction.SendInviteRequest(inviteRequest.GetRequestEndPoint(), inviteRequest);
            }
            catch (Exception excp)
            {
                logger.Error("Exception SendInviteRequest. " + excp.Message + ".");
            }
        }

        private void InviteFinalResponseReceived(IPEndPoint localEndPoint,  IPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse)
        {
            try
            {
                logger.Debug("FinalResponseReceived for INVITE to " + sipTransaction.InitialRequest.URI.ToString() + " " + sipResponse.Status + " " + sipResponse.ReasonPhrase + ".");
            }
            catch (Exception excp)
            {
                logger.Error("Exception InviteFinalResponseReceived. " + excp.Message);
            }
        }

        private SIPRequest GetInviteRequest(SIPURI dstURI, string callerIdName, string callerIdUser, string callerIdHost)
        {
            SIPRequest inviteRequest = new SIPRequest(SIPMethodsEnum.INVITE, dstURI);
            
            IPEndPoint localEndPoint = SIPTransport.GetTransportContact(null);
            string localAddressStr = localEndPoint.Address.ToString();

            SIPToHeader toHeader = SIPToHeader.ParseToHeader("<" + dstURI.ToString() + ">");

            SIPFromHeader fromHeader = SIPFromHeader.ParseFromHeader(callerIdName + " <sip:" + callerIdUser + "@" + callerIdHost + ">");
            fromHeader.FromTag = CallProperties.CreateNewTag();
            SIPContactHeader contact = SIPContactHeader.ParseContactHeader("sip:" + IPSocket.GetSocketString(localEndPoint))[0];

            SIPHeader inviteHeader = new SIPHeader(contact, fromHeader, toHeader, 1, CallProperties.CreateNewCallId());
            inviteHeader.CSeqMethod = SIPMethodsEnum.INVITE.ToString();
            inviteRequest.Header = inviteHeader;

            SIPViaHeader viaHeader = new SIPViaHeader(localEndPoint.Address.ToString(), localEndPoint.Port, CallProperties.CreateBranchId());
            inviteRequest.Header.Via.PushViaHeader(viaHeader);

            inviteRequest.Body =
                "v=0" + m_CRLF +
                "o=" + SERVER_STRING + " 613 888 IN IP4 " + localAddressStr + m_CRLF +
                "s=SIP Call" + m_CRLF +
                "c=IN IP4 " + localAddressStr + m_CRLF +
                "t=0 0" + m_CRLF +
                "m=audio 10000 RTP/AVP 0" + m_CRLF +
                "a=rtpmap:0 PCMU/8000" + m_CRLF;

            inviteRequest.Header.ContentLength = inviteRequest.Body.Length;
            inviteRequest.Header.ContentType = "application/sdp";

            return inviteRequest;
        }

        /// <summary>
        /// Place a call on hold from the web interface
        /// Note that incoming calls and outgoing calls can't be dealt with the same way since the re-invite request must be sent to different end points
        /// </summary>
        /// <param name="username">username</param>
        /// <param name="switchCallId">switchCallId</param>
        [OperationContract]
        public void HoldCall(string username, Guid switchCallId)
        {
            try
            {
                logger.Debug("Attempting to HoldCall for " + username + " and " + switchCallId.ToString() + ".");
                SIPSwitchCall call = SIPSwitchCall.GetCall(username, switchCallId);

                if (call != null)
                {
                    if (call.CALL_ON_HOLD == false) // if the call is already on hold, we must prevent the user to run this function
                    {
                        SIPRequest holdRequest;

                        if (call.SwitchedCall.IsOutgoingCall == true)   // the call is made by the SIP Switch user
                        {
                            logger.Debug("Call found to hold for " + username + " " + call.ServerTransaction.RequestURI.ToString() + ".");
                            FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call found to hold " + call.ServerTransaction.RequestURI.ToString(), username));
                            // Hold call: outgoing calls
                            holdRequest = call.PutOutgoingOnHold(call);
                        }
                        else                                            // the call is received by the SIP Switch user
                        {
                            logger.Debug("Call found to hold for " + username + " " + call.ServerTransaction.RequestFrom.URI.ToString() + ".");
                            FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call found to hold " + call.ServerTransaction.RequestFrom.URI.ToString(), username));
                            // Hold call: incoming calls
                            holdRequest = call.PutIncomingCallsOnHold(call, username);
                        }

                        // now that the request is prepared, we can send it
                        UACInviteTransaction inviteTransaction = new UACInviteTransaction(holdRequest);
                        inviteTransaction.TransactionFinalResponseReceived += new SIPTransactionResponseReceived(InviteFinalResponseReceived);
                        inviteTransaction.SendInviteRequest(holdRequest.GetRequestEndPoint(), holdRequest);

                        call.CALL_ON_HOLD = true;
                     }
                     else 
                     {
                         FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call already on hold ", username));
                     }
                 }
                 else
                 {
                     logger.Debug("Call not found to hold for " + username + " " + switchCallId.ToString() + ".");
                     FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call not found for hold request ", username));
                 }
             }
             catch (Exception excp)
             {
                 logger.Error("Exception HoldCall. " + excp.Message + ".");
             }
         }

        /// <summary>
         /// Retreive a call, specified by its username and callID, which was on hold for a specific username
         /// Note that incoming calls and outgoing calls can't be dealt with the same way since the re-invite request must be sent to different end points
         /// </summary>
         /// <param name="username">username</param>
         /// <param name="switchCallId">switchCallId</param>
        [OperationContract]
        public void RetreiveCall(string username, Guid switchCallId)
         {
             try
             {
                 logger.Debug("Attempting to RetreiveCall for " + username + " and " + switchCallId.ToString() + ".");
                 SIPSwitchCall call = SIPSwitchCall.GetCall(username, switchCallId);
              
                 if (call != null)
                 {
                     if (call.CALL_ON_HOLD == true) // if the call is not on hold we must not run the function
                     {
                         SIPRequest resumeRequest;

                         if (call.SwitchedCall.IsOutgoingCall == true) // once again we have to separate incoming from outgoing calls
                         {
                             logger.Debug("Call found to Retreive for " + username + " " + call.ServerTransaction.RequestURI.ToString() + ".");
                             FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call found to retreive " + call.ServerTransaction.RequestURI.ToString(), username));
                             // Hold call: outgoing calls
                             resumeRequest = call.ResumeOutgoingCallFromHold(call,username);
                         }
                         else
                         {
                             logger.Debug("Call found to Retreive for " + username + " " + call.ServerTransaction.RequestFrom.URI.ToString() + ".");
                             FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call found to retreive " + call.ServerTransaction.RequestFrom.URI.ToString(), username));
                             // Hold call: incoming calls
                             resumeRequest = call.ResumeIncomingCallFromHold(call, username);
                         }

                         UACInviteTransaction inviteTransaction = new UACInviteTransaction(resumeRequest);
                         inviteTransaction.TransactionFinalResponseReceived += new SIPTransactionResponseReceived(InviteFinalResponseReceived);
                         inviteTransaction.SendInviteRequest(resumeRequest.GetRequestEndPoint(), resumeRequest);
                         
                         call.CALL_ON_HOLD = false;
                    }
                    else
                    {
                        FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "The call is not on hold ", username));
                    }
                }
                else
                {
                    logger.Debug("Call not found to retreive for " + username + " " + switchCallId.ToString() + ".");
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call not found for retreive request ", username));
                }              
            }
            catch (Exception excp)
            {
                logger.Error("Exception RetreiveCall. " + excp.Message + ".");
            }           
        }

        [OperationContract]
        public void HangupCall(string username, Guid switchCallId)
        {
            try
            {
                logger.Debug("Attempting to HangupCall for " + username + " and " + switchCallId.ToString() + ".");

                SIPSwitchCall call = SIPSwitchCall.GetCall(username, switchCallId);

                if (call != null)
                {
                    logger.Debug("Call found to hangup for " + username + " " + call.ServerTransaction.RequestURI.ToString() + ".");
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call found to hangup " + call.ServerTransaction.RequestURI.ToString(), username));
                    call.HangupCall();
                }
                else
                {
                    logger.Debug("Call not found to hangup for " + username + " " + switchCallId.ToString() + ".");
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call not found for hold request", username));
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception HangupCall. " + excp.Message + ".");
            }
        }

        #region former transfer function
        /*
        /// <summary>
        /// Call unattended transfert:
        /// Sends a REFER request to the other call leg so it can initialise the transfer. The invite request to the 3rd party UA is not sent by the transferer but by its interlocutor
        /// For further details about SIP signalling for unattended transfer: http://www.tech-invite.com/Ti-sip-service-4.html
        /// Note that in order to perform an attended transfer, the SIP Switch user can place its interlocutor on hold, use a second line to call the 3rd person, 
        /// Then she can transfer the call while the caller is still on hold or she can resume the call first and then transfer.
        /// </summary>
        [OperationContract]
        public void TransferCall(string username, Guid switchCallId, string transferTo)
        {
            try
            {
                SIPSwitchCall call = SIPSwitchCall.GetCall(username, switchCallId);
                logger.Debug("Attempting to Transfer Call for " + call.ServerTransaction.RequestURI.ToString() + " to " + transferTo + ".");

                if (call != null)
                {
                    // REFER Request
                    SIPRequest referRequest = null;

                    if (call.SwitchedCall.IsOutgoingCall == true) // Once again we have to separate incoming from outgoing calls
                    {
                        referRequest = call.GetReferRequestOutgoing(call, username, transferTo);
                    }
                    else
                    {
                        referRequest = call.GetReferRequestIncoming(call, username, transferTo);
                    }

                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, " Transfer request from : " + referRequest.Header.From.FromURI.ToString(), username));
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, " Transfer request to : " + transferTo, username));

                    // Send the REFER request
                    IPEndPoint referEndPoint = referRequest.GetRequestEndPoint();
                    SIPTransaction.AddTransaction(referRequest);
                    SIPTransport.SendRequest(referEndPoint,referRequest);
                }
                else
                {
                    logger.Debug("Call not found to Transfer for " + username + " " + switchCallId.ToString() + ".");
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Call not found for transfer.", username));
                }
            }
            catch (Exception excp)
            {
                FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Exception preventes from transfer.", username));
                logger.Error("Exception TransferCall. " + excp.Message + ".");
            }
        }
         */
        #endregion

        /// <summary>
        /// If the call has not being established yet but needs to be transfered/forwarded to a 3rd party UA, the REFER request won't work.
        /// We need to use different functions. 
        /// </summary>
        /// <param name="username">Aauthentication name of the SIP Switch User</param>
        /// <param name="switchCallId">Call ID</param>
        /// <param name="forwardTo">3rd party number to forward the call to</param>
        [OperationContract]
        public void ForwardCall(string username, Guid switchCallId, string forwardTo)
        {
            try
            {
                SIPSwitchCall call = SIPSwitchCall.GetCall(username, switchCallId);
                if (call != null)
                {
                    call.ForwardCall(forwardTo);  
                }
                else
                {
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Exception forward to " + forwardTo + " call = null", username));
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ForwardCall. " + excp.Message + ".");
                FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Exception forward to " + forwardTo + " " + excp.Message, username));
            }
        }

        [OperationContract]
        public void TransferCall(string username, Guid switchCallId, string transferTo)
        {
            try
            {
                SIPSwitchCall call = SIPSwitchCall.GetCall(username, switchCallId);
                if (call != null)
                {
                    call.TransferCall(transferTo);  
                }
                else
                {
                    FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Exception transfer to " + transferTo + " call = null", username));
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ForwardCall. " + excp.Message + ".");
                FireProxyLogEvent(new ProxyMonitorEvent(ProxyServerTypesEnum.StatefulProxy, ProxyEventTypesEnum.NewMessage, "Exception transfer to " + transferTo + " " + excp.Message, username));
            }
        }

        private static void FireProxyLogEvent(ProxyMonitorEvent monitorEvent)
        {
            if (m_monitorChannel != null)
            {
                m_monitorChannel.Send(monitorEvent);
            }
                        
            if (StatefulProxyLogEvent != null)
            {
                try
                {
                    StatefulProxyLogEvent(monitorEvent);
                }
                catch (Exception excp)
                {
                    logger.Error("Exception FireProxyLogEvent SIPProxyWebService. " + excp.Message);
                }
            }
        }
    }
}

